#ifndef LPP_ENTERO_ENTERO_HPP
#define LPP_ENTERO_ENTERO_HPP
/**
 * @file 
 * @brief Main header of the Entero library.
 * @author Luis Machuca Bezzaza
 * @copyright GNU LGPL License 3.0 or later
 * */

// SPDX-License-Identifier: LGPL-3.0-or-later
// See LICENSE for copyright and warranty notices, COPYING for the full text of the license.

//! Informational: @a Entero library version.
#define LPP_ENTERO_VERSION 0x0001


#include <cxxomfort/base.hpp>
#if (CXXOMFORT_DATE < 20200918UL)
    #error "Update cxxomfort to at least 20200918 to use this library"
#endif

#include <cxxomfort/limits.hpp>
#include <cxxomfort/array.hpp>
#include <cxxomfort/algorithm.hpp>
#include <cxxomfort/impl/n3334-array_ref.hpp> // array_ref
#include "impl.hpp"

// 
// configuration macros
// 
#if (!defined(LPPENT_MAX_BYTES))
    /**
     * @brief Configuration: max allowed size @a Z for <tt>EnteroU<Z> , EnteroS<Z></tt>
     * 
     * This macro defaults to @c 4096 (4 kib) and is the maximum size, in bytes, that an @c EnteroU or @c EnteroS can be instantiated with. Instantiation of larger types, be it directly or indirectly (via eg.: wide multiplication) will result in a @c static_assert firing up.
     * 
     * */
    #define LPPENT_MAX_BYTES 4096
#elif (!defined(LPPENT_MAX_BYTES)
    #define LPPENT_MAX_BYTES 4096
#endif

#if (defined(DOXYGEN_DOC))
    /** 
     * @brief Configuration: disables @a Entero 's bitwise conversions to integral.
     * 
     * @a Entero types provide by default the binary operator <tt>uintmax_t operator& (EnteroU, uintmax_t)</tt> and <tt>intmax_t operator& (EnteroS, intmax_t)</tt> that allow to bitwise-mask, or narrow, an @a Entero value to an equivalent inetgral of the same signedness. This is useful, for example, when needing to convert an @a Entero that is known to have a small value, into an integral type variable.
     * 
     * If this macro is defined, the operators are disabled.
     * 
     * */
    #define LPPENT_DISABLE_NARROWING_AND
#endif

#if (defined(DOXYGEN_DOC))
    /** 
     * @brief Configuration: Entero size @a Z above which stream output will be forced in hexadecimal.
     * 
     * Operations like division and multiplication can be very costly, and because base conversion relies on them, stream output of @a Entero types can become even costlier aboe certain sizes. This macro, which defaults to the value @c 64 , defines the starting size for an @a Entero at which stream output (for example, to @c std::cout ) will @b always take place in hexadecimal notation, which is incredibly fast.
     * 
     * */
    #define LPPENT_MINZ_STARTS_HEX_IO 64
#elif !defined(LPPENT_MINZ_STARTS_HEX_IO)
    #define LPPENT_MINZ_STARTS_HEX_IO 64
#endif

namespace lpp { 
//! Hosts the @e Entero library.
namespace entero {

template <size_t S> class EnteroU;
template <size_t S> class EnteroS;
static const size_t MAX_Z = LPPENT_MAX_BYTES;

// 
// assists
// 

struct loader_lsf_tag {}; 
struct loader_msf_tag {};


// 
// Entero
// 

/**
 * @brief Type family that describe a multibyte @c unsigned integer.
 * @tparam unsigned:S The size of the integer, in bytes (assumed <tt>CHAR_BIT==8</tt>).
 * 
 * @note Currently in development - many features missing.
 * 
 * An @c EnteroU<S> , called @e Entero here for brevity, is a "bigint" type - in particular, it is an integer-like type representing numeric values with range and properties similar to those of C's native @c unsigned types except extended to a larger storage. A @c EnteroU<S> has a storage of @a S bytes = <tt>8*S</tt> bits in PC machines.
 * 
 * Given an @e Entero @c e of type @a E , and a C native unsigned @a u of any of their type varieties @e U (<tt>unsigned short, unsigned long</tt>, etc), some properties that hold for unsigned types also hold for @e Entero . In particular, 
 * given an alias type @e T that can be @a U or @a E :
 * 
 * @code
 * // both of them can be initialized with compiler integer expressions:
 * T var = 12345;
 * // the amount of information they can process 
 * // is solely given by their size: for T being U or E:
 * static_assert (std::numeric_limits<T>::digits == CHAR_BIT * sizeof(T), "!");
 * // their values (storage) can be copied over 
 * // with the following expression:
 * memcpy( destination, &var, sizeof(var) );
 * // they can be written or read from raw file streams 
 * // with the following expression:
 * fwrite( &var, sizeof(var), 1, handle );
 * fread( &var, sizeof(var), 1, handle );
 * 
 * @endcode
 * 
 * @section Operators 
 * 
 * @c EnteroU<S> supports the basic bitwise operators and the increment and decrement operators. Notable implementation aspects are noted here:
 * 
 * * <b>unary negation</b>:
 * 
 * <tt>EnteroU::operator- ()</tt> is intentionally not implemented.
 * 
 * * <b>Arthmetic operators</b>: 
 * 
 * Currently not implemented.
 * 
 * 
 * @section IO "I/O"
 * 
 * At this release, stream I/O has not yet been provided.
 * 
 * */
template <size_t S>
class EnteroU {
    static_assert( S < LPPENT_MAX_BYTES, "Entero too big breaks invariant: size < LPPENT_MAX_BYTES");
    private:
    typedef std::array<uint8_t,S> store_t;

    public:
    enum { I32_ = (bool)(S > sizeof(uint32_t)) };
    enum { IZ_ = (bool)(S > sizeof(uintmax_t)) , _MinZ = IZ_ ? sizeof(uintmax_t) : S };
    
    public:

    // 
    // constructors 
    // 

    // construct from a uintmax_t 
#if (defined(DOXYGEN_DOC))
    /**
     * @brief Creates an @a Entero of value is @a x, adjusted modulo for size.
     * 
     * @note @a x is of (@b unsigned! ) @c uintmax_t type.
     * 
     * @note This constructor is @c constexpr only starting with C++17.
     * 
     * @todo Future versions might increase @c constexpr capability to C++14 or C++11, depending on compiler-level limits (relaxed constexpr, recursion depth, etc).
     * 
     * */
    constexpr EnteroU (uintmax_t const x=0) noexcept {}

#elif (CXXO_COMPILER_SUPPORT_constexpr_relaxed)
    constexpr 
    explicit EnteroU (uintmax_t const x= 0) noexcept 
    : u_() {
        unsigned i=0; 
        for (;i < _MinZ; ++i) { u_[i]= x>>(CHAR_BIT*i); }
        for (; i < S; ++i) { u_[i]= 0; }
    }

#else
    explicit EnteroU (uintmax_t const x= 0) CXXO_NOEXCEPTNOTHROW 
    : u_() {
        //impl::array_ref<uint8_t> ru_ (u_);
        uint8_t const* px = reinterpret_cast<uint8_t const*>(&x);
        std::fill_n(std::begin(u_), S, 0);
        std::copy(px, px+_MinZ, std::begin(u_) );
    }

#endif

#if defined(DOXYGEN_DOC)
    //! Copy constructor
    constexpr EnteroU (EnteroU const&) noexcept = default;
    //! Copy assignment
    constexpr EnteroU& operator= (EnteroU const&) noexcept = default;
#else
    // operators trivially generated
#endif

    
    // 
    // static constructors 
    // 

    /**
     * @brief Creates an @e EnteroU value from the array @a px taken in least-significant-first order.
     * @param px A pointer to an array of at least @e S <tt>unsigned char</tt>.
     * */
    static 
    EnteroU make (loader_lsf_tag, uint8_t const* px) CXXO_NOEXCEPTNOTHROW {
        EnteroU ur;
        //memcpy(u.u_.data(), px, S);
        std::memcpy(ur.u_, px, S);
        return ur;
    }

    /**
     * @brief Creates an @e EnteroU value from the array @a px taken in most-significant-first order.
     * @param px A pointer to an array of at least @e S <tt>unsigned char</tt>.
     * */
    static 
    EnteroU make (loader_msf_tag, uint8_t const* px) CXXO_NOEXCEPTNOTHROW {
        EnteroU ur;
        //std::reverse_copy(px, px+S, ur.u_.begin());
        std::reverse_copy(px, px+S, std::begin(ur.u_));
        return ur;
    }
    
    // 
    // conversion 
    // 
    
    CXXO_EXPLICIT_OPERATOR(uintmax_t)() const CXXO_NOEXCEPTNOTHROW {
        return uintmax();
    }
    
    // 
    // unary operators 
    // 
    
    //! @return @c true if the current value is zero.
    bool operator! () const CXXO_NOEXCEPTNOTHROW {
        return *this==EnteroU(0);
    }
    
    //! @return The binary complement of the current value.
    EnteroU operator~ () CXXO_NOEXCEPTNOTHROW {
        EnteroU ret(*this);
        for (unsigned i=0; i < S; ++i) { ret.u_[i]= ~ret.u_[i]; }
        return ret;
    }

    //! Increments the current value.
    EnteroU& operator++ () CXXO_NOEXCEPTNOTHROW {
        return *this;        
    }

    //! Decrements the current value.
    EnteroU& operator-- () CXXO_NOEXCEPTNOTHROW {
        return *this;        
    }

    // 
    // binary= operators
    // 
    
    EnteroU& operator&= (EnteroU const& rhs) CXXO_NOEXCEPTNOTHROW {
        for (unsigned i=0; i < S; ++i) { u_[i]&= rhs.u_[i]; }
        return *this;        
    }

    EnteroU& operator|= (EnteroU const& rhs) CXXO_NOEXCEPTNOTHROW {
        for (unsigned i=0; i < S; ++i) { u_[i]|= rhs.u_[i]; }
        return *this;        
    }

    EnteroU& operator^= (EnteroU const& rhs) CXXO_NOEXCEPTNOTHROW {
        for (unsigned i=0; i < S; ++i) { u_[i]^= rhs.u_[i]; }
        return *this;        
    }
    
    EnteroU&    operator&= (uintmax_t u) CXXO_NOEXCEPTNOTHROW {
        *this&= EnteroU(u);
		return *this;
	}

    EnteroU&    operator|= (uintmax_t u) CXXO_NOEXCEPTNOTHROW {
        *this|= EnteroU(u);
		return *this;
	}

    EnteroU&    operator^= (uintmax_t u) CXXO_NOEXCEPTNOTHROW {
        *this^= EnteroU(u);
		return *this;
	}

    // 
    // bitwise shift 
    // 
    
    CXXO_CONSTEXPR14 
    EnteroU& operator<<= (size_t K) CXXO_NOEXCEPTNOTHROW {
		impl::EntOps<>::lshift(u_,K);
        return *this;
    }

    CXXO_CONSTEXPR14 
    EnteroU&    operator>>=     (size_t K) CXXO_NOEXCEPTNOTHROW {
        impl::array_ref<uint8_t> ru_= impl::arrayref<uint8_t>(u_);
		if (K >= CHAR_BIT*S) {
            *this= EnteroU(0);
        } else {
            impl::EntOps<>::rshift(ru_,K);
        }
        return *this;
    }
    

    
    // 
    // math= operators
    // 
    
    
    // 
    // named members 
    // 

    uintmax_t uintmax () const CXXO_NOEXCEPTNOTHROW {
        //return impl::IntOps<S>::to_u(this->u_);
        return impl::EntOps<>::to_u(impl::carrayref(u_));
    }
    
    int   leftmost1 () const CXXO_NOEXCEPTNOTHROW {
        return impl::EntOps<>::l_1_bit(impl::carrayref(u_));
    }

    // 
    // friend impl, data access 
    // 

    //! Compares two @e EnteroU s for equality.
    friend CXXO_CONSTEXPR
    bool operator== (EnteroU const& N1, EnteroU const& N2) CXXO_NOEXCEPTNOTHROW {
        return N1.u_ == N2.u_;
    }

    //! Compares two @e EnteroU s for less-than.
    friend CXXO_CONSTEXPR
    bool operator< (EnteroU const& N1, EnteroU const& N2) CXXO_NOEXCEPTNOTHROW {
        return impl::lsf_compare( impl::carrayref(N1.u_), impl::carrayref(N2.u_) ) < 0;
    }
    
    //! Provides access to the binary data of the given @e EnteroU .
    friend CXXO_CONSTEXPR 
    uint8_t const* data (EnteroU const& E) CXXO_NOEXCEPTNOTHROW { 
        return E.u_.data();
    }

    // 
    // private 
    // 
    
    private:
    store_t u_;
};


/**
 * @brief Type family that describe a multibyte @c signed integer.
 * @tparam unsigned:S The size of the integer, in bytes (assumed <tt>CHAR_BIT==8</tt>).
 * 
 * An @c EnteroS<S> , called @e Entero here for brevity, is a "bigint" type - in particular, it is an integer-like type representing numeric values with range and properties similar to those of C's native @c signed types, extended to a larger storage. A @c EnteroS<S> has a storage of @a S bytes = <tt>8*S</tt> bits in PC machines.
 * 
 * Given an @e Entero @c e of type @a E , and a C native signed variable  @a u of any of their type varieties @e U (<tt>signed short, signed long</tt>, etc), some properties that hold for unsigned types also hold for @e Entero . In particular, 
 * given an alias type @e T that can be @a U or @a E :
 * 
 * @code
 * // both of them can be initialized with compiler integer expressions:
 * T var = 12345;
 * // the amount of information they can process 
 * // is solely given by their size: for T being U or E:
 * static_assert (std::numeric_limits<T>::digits + 1 == CHAR_BIT * sizeof(T), "!");
 * // their values (storage) can be copied over 
 * // with the following expression:
 * memcpy( destination, &var, sizeof(var) );
 * // they can be written or read from raw file streams 
 * // with the following expression:
 * fwrite( &var, sizeof(var), 1, handle );
 * fread( &var, sizeof(var), 1, handle );
 * 
 * @endcode
 * 
 * 
 * */
template <size_t S>
class EnteroS
: private EnteroU<S> {
    private:
    typedef EnteroU<S> Base;

    public:
    enum { I32_ = (bool)(S > sizeof(uint32_t)) };
    enum { IZ_ = (bool)(S > sizeof(uintmax_t)) , _MinZ = IZ_ ? sizeof(uintmax_t) : S };
    
    public:


};


// 
// comparison operators 
// 

#define TPARAMS size_t Z
#define TARGS Z
CXXO_GENERATE_RELATIONALS_T(EnteroU);
CXXO_GENERATE_RELATIONALS_T(EnteroS);
#undef TARGS
#undef TPARAMS



// 
// bitwise ops
// 

template <size_t Z>
EnteroU<Z> operator& (EnteroU<Z> n1, EnteroU<Z> const n2) CXXO_NOEXCEPTNOTHROW {
    return n1&= n2;
}

template <size_t Z>
EnteroU<Z> operator| (EnteroU<Z> n1, EnteroU<Z> const n2) CXXO_NOEXCEPTNOTHROW {
    return n1|= n2;
}

template <size_t Z>
EnteroU<Z> operator^ (EnteroU<Z> n1, EnteroU<Z> const n2) CXXO_NOEXCEPTNOTHROW {
    return n1^= n2;
}


// 
// arithmetic ops
// 


// 
// operators coordinating with integer types 
// 

} } // lpp::Entero


#if (CXXOMFORT_CXX_STD >= 2011)
namespace lpp { namespace entero {
    
} }
#endif

// 
// numeric_limits 
// 

//! namespace std, for @c numeric_limits etc.
namespace std {

//! Specialization of <tt>std::numeric_limits</tt> for @a EnteroU types.
template <size_t Z>
struct numeric_limits< lpp::entero::EnteroU<Z> >
: public numeric_limits<unsigned> {
    static CXXO_CONSTEXPR const int digits = Z*CHAR_BIT;
    static CXXO_CONSTEXPR const int digits10 = static_cast<int>((Z+1) * 2.4); // Z * log256/log10 + 1
};

//! Specialization of <tt>std::numeric_limits</tt>.
template <size_t Z>
struct numeric_limits< lpp::entero::EnteroS<Z> >
: public numeric_limits<signed> {
    static CXXO_CONSTEXPR const int digits = Z*CHAR_BIT-1;
    static CXXO_CONSTEXPR const int digits10 = static_cast<int>((Z+1) * 2.4);
};

}


#endif

/**
 * @mainpage Entero
 * 
 * A package that extends the concept of C integrals to any size.
 * 
 * @a Entero 's purpose can be summarized as such:
 * 
 * <blockquote>\"When in the land of the @c int s, do as the @c int s do"</blockquote>
 * 
 @e Entero is a a C++ "bigint" library that provides two type families, @c EnteroU and @c EnteroS , that extend the concept of "integral types" from the C-native ones to integrals of any size (in bytes). Functionally speaking, a @c EnteroS<N> is an @a N -bytes sized @c int type and will share most properties with the native @c int .
 * 
 * The type family @link lpp::entero::EnteroU @c EnteroU<N> @endlink extends the concept of @c unsigned types, and for the most part can interoperate with all of C's unsigned types.
 * 
 * The type family @link lpp::entero::EnteroS @c EnteroS<N> @endlink extends the concept of @c signed types, and for the most part can interoperate with all of C's signed types.
 * 
 * As the saying goes, these types aim to behave as if they could conceivably be "native" integral types of the given size, so they opt in or out of certain behaviours lirbaries in C++ usually have. In particular, for an @a Entero type:
 * 
 * * the types have an exact, predefined size
 * * objects do not use or request dynamically allocated storage
 * * @b all constructors and assignment operators are @c noexcept / no-throw
 * * objects can always be initialized to a C integral value
 * * no arithmethic operations throw (not even division by zero)
 * 
 * At the same time, there are a few differences owing to inconsistencies and misfeatures in the C/C++ implementation of integral types:
 * 
 * * @a Entero s always default-initialize to the numeric value zero
 * * no implicit conversion between signed and unsigned @a Entero s
 * * no implicit narrowing from a large @a Entero to a "baseline" @a Entero , nor to an integral type
 * 
 * As of this release, @a Entero is not in production status and is intended mostly as a proof-of-concept. It is not expected to have high performance compared to other bigint libraries, except for the fact that unlike libraries like GMP, @a Entero integers do not rely on dynamic memory allocation (<em>because ints don't</em>). In particular, the types are not assembler-optimized for any architecture because that deviates (strongly) from the common language that is C/C++.
 * 
 * 
 * */
